// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h"

#include <stddef.h>
#include <stdint.h>

#include <utility>

#include "base/debug/crash_logging.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_handle.h"
#include "content/child/dwrite_font_proxy/dwrite_localized_strings_win.h"
#include "content/common/dwrite_font_proxy_messages.h"
#include "content/public/child/child_thread.h"
#include "ipc/ipc_platform_file.h"
#include "ipc/ipc_sender.h"

namespace mswr = Microsoft::WRL;

namespace content {

namespace {

    // This enum is used to define the buckets for an enumerated UMA histogram.
    // Hence,
    //   (a) existing enumerated constants should never be deleted or reordered, and
    //   (b) new constants should only be appended at the end of the enumeration.
    enum DirectWriteLoadFamilyResult {
        LOAD_FAMILY_SUCCESS_SINGLE_FAMILY = 0,
        LOAD_FAMILY_SUCCESS_MATCHED_FAMILY = 1,
        LOAD_FAMILY_ERROR_MULTIPLE_FAMILIES = 2,
        LOAD_FAMILY_ERROR_NO_FAMILIES = 3,
        LOAD_FAMILY_ERROR_NO_COLLECTION = 4,

        LOAD_FAMILY_MAX_VALUE
    };

    // This enum is used to define the buckets for an enumerated UMA histogram.
    // Hence,
    //   (a) existing enumerated constants should never be deleted or reordered, and
    //   (b) new constants should only be appended at the end of the enumeration.
    enum FontProxyError {
        FIND_FAMILY_SEND_FAILED = 0,
        GET_FAMILY_COUNT_SEND_FAILED = 1,
        COLLECTION_KEY_INVALID = 2,
        FAMILY_INDEX_OUT_OF_RANGE = 3,
        GET_FONT_FILES_SEND_FAILED = 4,
        MAPPED_FILE_FAILED = 5,
        DUPLICATE_HANDLE_FAILED = 6,

        FONT_PROXY_ERROR_MAX_VALUE
    };

    const char kFontKeyName[] = "font_key_name";

    void LogLoadFamilyResult(DirectWriteLoadFamilyResult result)
    {
        UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.LoadFamilyResult", result,
            LOAD_FAMILY_MAX_VALUE);
    }

    void LogFamilyCount(uint32_t count)
    {
        UMA_HISTOGRAM_COUNTS_1000("DirectWrite.Fonts.Proxy.FamilyCount", count);
    }

    void LogFontProxyError(FontProxyError error)
    {
        UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.FontProxyError", error,
            FONT_PROXY_ERROR_MAX_VALUE);
    }

} // namespace

DWriteFontCollectionProxy::DWriteFontCollectionProxy() = default;

DWriteFontCollectionProxy::~DWriteFontCollectionProxy() = default;

HRESULT DWriteFontCollectionProxy::FindFamilyName(const WCHAR* family_name,
    UINT32* index,
    BOOL* exists)
{
    DCHECK(family_name);
    DCHECK(index);
    DCHECK(exists);
    TRACE_EVENT0("dwrite", "FontProxy::FindFamilyName");

    uint32_t family_index = 0;
    base::string16 name(family_name);

    auto iter = family_names_.find(name);
    if (iter != family_names_.end()) {
        *index = iter->second;
        *exists = iter->second != UINT_MAX;
        return S_OK;
    }

    if (!GetSender()->Send(
            new DWriteFontProxyMsg_FindFamily(name, &family_index))) {
        LogFontProxyError(FIND_FAMILY_SEND_FAILED);
        return E_FAIL;
    }

    if (family_index != UINT32_MAX) {
        if (!CreateFamily(family_index))
            return E_FAIL;
        *exists = TRUE;
        *index = family_index;
        families_[family_index]->SetName(name);
    } else {
        *exists = FALSE;
        *index = UINT32_MAX;
    }

    family_names_[name] = *index;
    return S_OK;
}

HRESULT DWriteFontCollectionProxy::GetFontFamily(
    UINT32 index,
    IDWriteFontFamily** font_family)
{
    DCHECK(font_family);

    if (index < families_.size() && families_[index]) {
        families_[index].CopyTo(font_family);
        return S_OK;
    }

    if (!CreateFamily(index))
        return E_FAIL;

    families_[index].CopyTo(font_family);
    return S_OK;
}

UINT32 DWriteFontCollectionProxy::GetFontFamilyCount()
{
    if (family_count_ != UINT_MAX)
        return family_count_;

    TRACE_EVENT0("dwrite", "FontProxy::GetFontFamilyCount");

    uint32_t family_count = 0;
    if (!GetSender()->Send(
            new DWriteFontProxyMsg_GetFamilyCount(&family_count))) {
        LogFontProxyError(GET_FAMILY_COUNT_SEND_FAILED);
        return 0;
    }

    LogFamilyCount(family_count);
    family_count_ = family_count;
    return family_count;
}

HRESULT DWriteFontCollectionProxy::GetFontFromFontFace(
    IDWriteFontFace* font_face,
    IDWriteFont** font)
{
    DCHECK(font_face);
    DCHECK(font);

    for (const auto& family : families_) {
        if (family && family->GetFontFromFontFace(font_face, font)) {
            return S_OK;
        }
    }
    // If the font came from our collection, at least one family should match
    DCHECK(false);

    return E_FAIL;
}

HRESULT DWriteFontCollectionProxy::CreateEnumeratorFromKey(
    IDWriteFactory* factory,
    const void* collection_key,
    UINT32 collection_key_size,
    IDWriteFontFileEnumerator** font_file_enumerator)
{
    if (!collection_key || collection_key_size != sizeof(uint32_t)) {
        LogFontProxyError(COLLECTION_KEY_INVALID);
        return E_INVALIDARG;
    }

    TRACE_EVENT0("dwrite", "FontProxy::LoadingFontFiles");

    const uint32_t* family_index = reinterpret_cast<const uint32_t*>(collection_key);

    if (*family_index >= GetFontFamilyCount()) {
        LogFontProxyError(FAMILY_INDEX_OUT_OF_RANGE);
        return E_INVALIDARG;
    }

    // If we already loaded the family we should reuse the existing collection.
    DCHECK(!families_[*family_index]->IsLoaded());

    std::vector<base::string16> file_names;
    std::vector<IPC::PlatformFileForTransit> file_handles;
    if (!GetSender()->Send(new DWriteFontProxyMsg_GetFontFiles(
            *family_index, &file_names, &file_handles))) {
        LogFontProxyError(GET_FONT_FILES_SEND_FAILED);
        return E_FAIL;
    }

    std::vector<HANDLE> handles;
    file_handles.reserve(file_names.size() + file_handles.size());
    for (const base::string16& file_name : file_names) {
        // This leaks the handles, since they are used as the reference key to
        // CreateStreamFromKey, and DirectWrite requires the reference keys to
        // remain valid for the lifetime of the loader. The loader is the font
        // collection proxy, which remains alive for the lifetime of the renderer.
        HANDLE handle = CreateFile(file_name.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (handle != INVALID_HANDLE_VALUE)
            handles.push_back(handle);
    }
    for (const IPC::PlatformFileForTransit& file_handle : file_handles) {
        handles.push_back(IPC::PlatformFileForTransitToPlatformFile(file_handle));
    }

    HRESULT hr = mswr::MakeAndInitialize<FontFileEnumerator>(
        font_file_enumerator, factory, this, &handles);

    if (!SUCCEEDED(hr)) {
        DCHECK(false);
        return E_FAIL;
    }

    return S_OK;
}

HRESULT DWriteFontCollectionProxy::CreateStreamFromKey(
    const void* font_file_reference_key,
    UINT32 font_file_reference_key_size,
    IDWriteFontFileStream** font_file_stream)
{
    if (font_file_reference_key_size != sizeof(HANDLE)) {
        return E_FAIL;
    }

    TRACE_EVENT0("dwrite", "FontFileEnumerator::CreateStreamFromKey");

    HANDLE file_handle = *reinterpret_cast<const HANDLE*>(font_file_reference_key);

    if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) {
        DCHECK(false);
        return E_FAIL;
    }

    mswr::ComPtr<FontFileStream> stream;
    if (!SUCCEEDED(
            mswr::MakeAndInitialize<FontFileStream>(&stream, file_handle))) {
        DCHECK(false);
        return E_FAIL;
    }
    *font_file_stream = stream.Detach();
    return S_OK;
}

HRESULT DWriteFontCollectionProxy::RuntimeClassInitialize(
    IDWriteFactory* factory,
    IPC::Sender* sender_override)
{
    DCHECK(factory);

    factory_ = factory;
    sender_override_ = sender_override;

    HRESULT hr = factory->RegisterFontCollectionLoader(this);
    DCHECK(SUCCEEDED(hr));
    hr = factory_->RegisterFontFileLoader(this);
    DCHECK(SUCCEEDED(hr));
    return S_OK;
}

void DWriteFontCollectionProxy::Unregister()
{
    factory_->UnregisterFontCollectionLoader(this);
    factory_->UnregisterFontFileLoader(this);
}

bool DWriteFontCollectionProxy::LoadFamily(
    UINT32 family_index,
    IDWriteFontCollection** containing_collection)
{
    TRACE_EVENT0("dwrite", "FontProxy::LoadFamily");

    uint32_t index = family_index;
    // CreateCustomFontCollection ends up calling
    // DWriteFontCollectionProxy::CreateEnumeratorFromKey.
    HRESULT hr = factory_->CreateCustomFontCollection(
        this /*collectionLoader*/, reinterpret_cast<const void*>(&index),
        sizeof(index), containing_collection);

    return SUCCEEDED(hr);
}

bool DWriteFontCollectionProxy::GetFontFamily(UINT32 family_index,
    const base::string16& family_name,
    IDWriteFontFamily** font_family)
{
    DCHECK(font_family);
    DCHECK(!family_name.empty());
    if (!CreateFamily(family_index))
        return false;

    mswr::ComPtr<DWriteFontFamilyProxy>& family = families_[family_index];
    if (!family->IsLoaded() || family->GetName().empty())
        family->SetName(family_name);

    family.CopyTo(font_family);
    return true;
}

bool DWriteFontCollectionProxy::LoadFamilyNames(
    UINT32 family_index,
    IDWriteLocalizedStrings** localized_strings)
{
    TRACE_EVENT0("dwrite", "FontProxy::LoadFamilyNames");

    std::vector<std::pair<base::string16, base::string16>> strings;
    if (!GetSender()->Send(
            new DWriteFontProxyMsg_GetFamilyNames(family_index, &strings))) {
        return false;
    }

    HRESULT hr = mswr::MakeAndInitialize<DWriteLocalizedStrings>(
        localized_strings, &strings);

    return SUCCEEDED(hr);
}

bool DWriteFontCollectionProxy::CreateFamily(UINT32 family_index)
{
    if (family_index < families_.size() && families_[family_index])
        return true;

    UINT32 family_count = GetFontFamilyCount();
    if (family_index >= family_count) {
        return false;
    }

    if (families_.size() < family_count)
        families_.resize(family_count);

    mswr::ComPtr<DWriteFontFamilyProxy> family;
    HRESULT hr = mswr::MakeAndInitialize<DWriteFontFamilyProxy>(&family, this,
        family_index);
    DCHECK(SUCCEEDED(hr));
    DCHECK_LT(family_index, families_.size());

    families_[family_index] = family;
    return true;
}

IPC::Sender* DWriteFontCollectionProxy::GetSender()
{
    return sender_override_ ? sender_override_ : ChildThread::Get();
}

DWriteFontFamilyProxy::DWriteFontFamilyProxy() = default;

DWriteFontFamilyProxy::~DWriteFontFamilyProxy() = default;

HRESULT DWriteFontFamilyProxy::GetFontCollection(
    IDWriteFontCollection** font_collection)
{
    DCHECK(font_collection);

    proxy_collection_.CopyTo(font_collection);
    return S_OK;
}

UINT32 DWriteFontFamilyProxy::GetFontCount()
{
    // We could conceivably proxy just the font count. However, calling
    // GetFontCount is almost certain to be followed by a series of GetFont
    // calls which will need to load all the fonts anyway, so we might as
    // well save an IPC here.
    if (!LoadFamily())
        return 0;

    return family_->GetFontCount();
}

HRESULT DWriteFontFamilyProxy::GetFont(UINT32 index, IDWriteFont** font)
{
    DCHECK(font);

    if (index >= GetFontCount()) {
        return E_INVALIDARG;
    }
    if (!LoadFamily())
        return E_FAIL;

    return family_->GetFont(index, font);
}

HRESULT DWriteFontFamilyProxy::GetFamilyNames(IDWriteLocalizedStrings** names)
{
    DCHECK(names);

    // Prefer the real thing, if available.
    if (family_) {
        family_names_.Reset(); // Release cached data.
        return family_->GetFamilyNames(names);
    }

    // If already cached, use the cache.
    if (family_names_) {
        family_names_.CopyTo(names);
        return S_OK;
    }

    TRACE_EVENT0("dwrite", "FontProxy::GetFamilyNames");

    // Otherwise, do the IPC.
    if (!proxy_collection_->LoadFamilyNames(family_index_, &family_names_))
        return E_FAIL;

    family_names_.CopyTo(names);
    return S_OK;
}

HRESULT DWriteFontFamilyProxy::GetFirstMatchingFont(
    DWRITE_FONT_WEIGHT weight,
    DWRITE_FONT_STRETCH stretch,
    DWRITE_FONT_STYLE style,
    IDWriteFont** matching_font)
{
    DCHECK(matching_font);

    if (!LoadFamily())
        return E_FAIL;

    return family_->GetFirstMatchingFont(weight, stretch, style, matching_font);
}

HRESULT DWriteFontFamilyProxy::GetMatchingFonts(
    DWRITE_FONT_WEIGHT weight,
    DWRITE_FONT_STRETCH stretch,
    DWRITE_FONT_STYLE style,
    IDWriteFontList** matching_fonts)
{
    DCHECK(matching_fonts);

    if (!LoadFamily())
        return E_FAIL;

    return family_->GetMatchingFonts(weight, stretch, style, matching_fonts);
}

HRESULT DWriteFontFamilyProxy::RuntimeClassInitialize(
    DWriteFontCollectionProxy* collection,
    UINT32 index)
{
    DCHECK(collection);

    proxy_collection_ = collection;
    family_index_ = index;
    return S_OK;
}

bool DWriteFontFamilyProxy::GetFontFromFontFace(IDWriteFontFace* font_face,
    IDWriteFont** font)
{
    DCHECK(font_face);
    DCHECK(font);

    if (!family_)
        return false;

    mswr::ComPtr<IDWriteFontCollection> collection;
    HRESULT hr = family_->GetFontCollection(&collection);
    DCHECK(SUCCEEDED(hr));
    hr = collection->GetFontFromFontFace(font_face, font);

    return SUCCEEDED(hr);
}

void DWriteFontFamilyProxy::SetName(const base::string16& family_name)
{
    family_name_.assign(family_name);
}

const base::string16& DWriteFontFamilyProxy::GetName()
{
    return family_name_;
}

bool DWriteFontFamilyProxy::IsLoaded()
{
    return family_ != nullptr;
}

bool DWriteFontFamilyProxy::LoadFamily()
{
    if (family_)
        return true;

    SCOPED_UMA_HISTOGRAM_TIMER("DirectWrite.Fonts.Proxy.LoadFamilyTime");

    base::debug::ScopedCrashKey crash_key(kFontKeyName,
        base::WideToUTF8(family_name_));

    mswr::ComPtr<IDWriteFontCollection> collection;
    if (!proxy_collection_->LoadFamily(family_index_, &collection)) {
        LogLoadFamilyResult(LOAD_FAMILY_ERROR_NO_COLLECTION);
        return false;
    }

    UINT32 family_count = collection->GetFontFamilyCount();

    HRESULT hr;
    if (family_count > 1) {
        // Some fonts are packaged in a single file containing multiple families. In
        // such a case we can find the right family by family name.
        DCHECK(!family_name_.empty());
        UINT32 family_index = 0;
        BOOL found = FALSE;
        hr = collection->FindFamilyName(family_name_.c_str(), &family_index, &found);
        if (SUCCEEDED(hr) && found) {
            hr = collection->GetFontFamily(family_index, &family_);
            LogLoadFamilyResult(LOAD_FAMILY_SUCCESS_MATCHED_FAMILY);
            return SUCCEEDED(hr);
        }
    }

    DCHECK_LE(family_count, 1u);

    if (family_count == 0) {
        // This is really strange, we successfully loaded no fonts?!
        LogLoadFamilyResult(LOAD_FAMILY_ERROR_NO_FAMILIES);
        return false;
    }

    LogLoadFamilyResult(family_count == 1 ? LOAD_FAMILY_SUCCESS_SINGLE_FAMILY
                                          : LOAD_FAMILY_ERROR_MULTIPLE_FAMILIES);

    hr = collection->GetFontFamily(0, &family_);

    return SUCCEEDED(hr);
}

FontFileEnumerator::FontFileEnumerator() = default;

FontFileEnumerator::~FontFileEnumerator() = default;

HRESULT FontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** file)
{
    DCHECK(file);
    if (current_file_ >= files_.size()) {
        return E_FAIL;
    }

    TRACE_EVENT0("dwrite", "FontFileEnumerator::GetCurrentFontFile");

    // CreateCustomFontFileReference ends up calling
    // DWriteFontCollectionProxy::CreateStreamFromKey.
    HRESULT hr = factory_->CreateCustomFontFileReference(
        reinterpret_cast<const void*>(&files_[current_file_]),
        sizeof(files_[current_file_]), loader_.Get() /*IDWriteFontFileLoader*/,
        file);
    DCHECK(SUCCEEDED(hr));
    return hr;
}

HRESULT FontFileEnumerator::MoveNext(BOOL* has_current_file)
{
    DCHECK(has_current_file);

    TRACE_EVENT0("dwrite", "FontFileEnumerator::MoveNext");
    if (next_file_ >= files_.size()) {
        *has_current_file = FALSE;
        current_file_ = UINT_MAX;
        return S_OK;
    }

    current_file_ = next_file_;
    ++next_file_;
    *has_current_file = TRUE;
    return S_OK;
}

HRESULT FontFileEnumerator::RuntimeClassInitialize(
    IDWriteFactory* factory,
    IDWriteFontFileLoader* loader,
    std::vector<HANDLE>* files)
{
    factory_ = factory;
    loader_ = loader;
    files_.swap(*files);
    return S_OK;
}

FontFileStream::FontFileStream() = default;

FontFileStream::~FontFileStream() = default;

HRESULT FontFileStream::GetFileSize(UINT64* file_size)
{
    *file_size = data_.length();
    return S_OK;
}

HRESULT FontFileStream::GetLastWriteTime(UINT64* last_write_time)
{
    *last_write_time = 0;
    return S_OK;
}

HRESULT FontFileStream::ReadFileFragment(const void** fragment_start,
    UINT64 fragment_offset,
    UINT64 fragment_size,
    void** fragment_context)
{
    if (fragment_offset + fragment_size < fragment_offset)
        return E_FAIL;
    if (fragment_offset + fragment_size > data_.length())
        return E_FAIL;
    *fragment_start = data_.data() + fragment_offset;
    *fragment_context = nullptr;
    return S_OK;
}

HRESULT FontFileStream::RuntimeClassInitialize(HANDLE handle)
{
    // Duplicate the original handle so we can reopen the file after the memory
    // mapped section closes it.
    HANDLE duplicate_handle;
    if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
            &duplicate_handle, 0 /* dwDesiredAccess */,
            false /* bInheritHandle */, DUPLICATE_SAME_ACCESS)) {
        LogFontProxyError(DUPLICATE_HANDLE_FAILED);
        return E_FAIL;
    }

    data_.Initialize(base::File(duplicate_handle));
    if (!data_.IsValid()) {
        LogFontProxyError(MAPPED_FILE_FAILED);
        return E_FAIL;
    }
    return S_OK;
}

} // namespace content
